#include "Taak.h"
#include "logger.hpp"

int DEFAULT_FUZZY_THRESHOLD = 50; //for fuzzy search, in terms of percentage
extern loglevel_enum loglevel;


const static string FILE_NAME = "Taak.txt";
// for displaying
const static string UPCOMING_TASKS_TITLE = "<h3><stong>Upcoming Tasks:</strong><\h3>";
static const string SORT_RESULTS_TITLE = "<h3>Sort Results:<\h3>";
const static string SEARCH_RESULTS_TITLE = "<h3>Search Results:<\h3>";
const static string FILTER_RESULT_TITLE = "<h3>Filtered Results:<\h3>";

const static string WELCOME_STATUS = "<span style=\"color:#333333\">Welcome to Taak</span>";
const static string WELCOME_ERROR_STATUS = "<span style=\"color:#333333\">Welcome to Taak. Error in file detected</span>";
const static string CLEAR_COMMAND_STATUS = "<h2>There are currently no Tasks. Add one now!<\h2>";
const static string NEW_TASK_ADDED_STATUS = "New Task \"%s\" added";
const static string DISPLAY_COMMAND_STATUS = "Display";
const static string UPDATE_COMMAND_1_STATUS = "Now you can update the task";
const static string UPDATE_COMMAND_2_STATUS ="Successfully modified from \"%s\" to \"%s\"";
const static string NOTHING_UNDO_STATUS = "Nothing to undo now!";
const static string UNDO_FINISH_STATUS = "Undo finished!";
const static string NOTHING_REDO_STATUS = "Nothing to redo!";
const static string REDO_FINISH_STATUS = "Redo finished!";
const static string HELP_COMMAND_STATUS = "help info";
const static string MAJOR_ERROR_STATUS = "An error occurred!";

const static string NO_RESULTS_DISPLAY = "No results were found.<br>";


//@author A0096566M
int Taak::initialize(){
	log(LOG_INFO) << "Taak::Initialize - Initializing Taak";

	bool containInvalidRecord;
	file.setFileName(FILE_NAME);
	CmdHelp::loadHelpInfo();
	containInvalidRecord = taskRecords.initialize(&file);
	parser.setTaskListRecords(&taskRecords);
	initialRecords = taskRecords;
	if (initialRecords.getRecordSize() <= 0) {
		userInput(Parser::COMMAND_HELP);
	}
	else {
		userInput(Parser::COMMAND_DISPLAY);
	}
	if (!containInvalidRecord) {
		setStatusDisplay(WELCOME_STATUS);
	} else {
		setStatusDisplay(WELCOME_ERROR_STATUS);
	}
	parser.setDisplayRecords(&displayedRecords);

	log(LOG_INFO) << "Taak::Initialize - Taak Initialized";
	return 0;
}
//@author A0096566M
string Taak::userInput(string input){
	log(LOG_INFO) << "Taak::userInput - Got input: " << input;
	try{
		log(LOG_INFO) << "Taak::userInput - Parsing";
		auto_ptr<Cmd> cmd = parser.parse(input);
		log(LOG_INFO) << "Taak::userInput - Executing";
		string ret = executeCommand(cmd);
		log(LOG_INFO) << "Taak::userInput - Done Executing";
		return ret;
	} catch(...){
		this->setStatusDisplay(MAJOR_ERROR_STATUS);
		log(LOG_WARNING) << "Taak::userInput - Exception";
		return "";
	}
}
//@author A0096566M
string Taak::userCharInput(string input){
	log(LOG_INFO) << "Taak::userCharInput - Got input: " << input;
	try{
		log(LOG_INFO) << "Taak::userCharInput - Getting feedback";
		setFeedbackDisplay(parser.getFeedback(input));
		log(LOG_INFO) << "Taak::userCharInput - Got and set feedback";
	} catch(...){
		this->setStatusDisplay(MAJOR_ERROR_STATUS);
		log(LOG_WARNING) << "Taak::userCharInput - Exception";
	}
	return "";
}
//@author A0096566M
string Taak::userSuggestionInput(string input, Cmd* command){
	log(LOG_DEBUG) << "Taak::SuggestionInput - Got command.";
	if(command->getCmdType() != INVALID){
		log(LOG_DEBUG) << "Taak::userSuggestionInput - Valid command.";
		try{
			log(LOG_DEBUG) << "Taak::userSuggestionInput - Executing command";
			string ret = executeCommand(auto_ptr<Cmd>(command));
			log(LOG_DEBUG) << "Taak::userSuggestionInput - Done Executing command";
			return ret;
		}catch(...){
			this->setStatusDisplay(MAJOR_ERROR_STATUS);
			log(LOG_DEBUG) << "Taak::userSuggestionInput - Exception";
		}
	} else {
		log(LOG_DEBUG) << "Taak::userSuggestionInput - Invalid command, parse as usual.";
		return userInput(input);
	}
}

// @author A0105541U
// Home page is defined as the page where all upcoming tasks are displayed
void Taak::displayHomePage() {
	Records upcomingRecords = taskRecords.filterByDone(true, false).sortByDateEnd(true);
	displayedRecords = upcomingRecords;
	setMainDisplay(upcomingRecords.toString());
    setMainDisplayTitle(UPCOMING_TASKS_TITLE);
}

// @author A0105541U
// After adding a task, the home screen is shown.
string Taak::executeAddCommand(CmdAdd* cmd) {
	log(LOG_INFO) << "Taak::executeAddCommand - Begin to add a task";
	Task task = cmd->getTask();
	taskRecords.addData(task);
	sprintf_s(buffer, NEW_TASK_ADDED_STATUS.c_str(), task.toScreenString().c_str());
	setStatusDisplay(buffer);
	displayHomePage();
	return buffer;
}

//@author A0105541U
string Taak::executeDeleteCommand(CmdDel* cmd) {
	log(LOG_DEBUG) << "In executeDeleteCommand";
	vector<int> ids;
	myCopyVector<int>(ids, cmd->ids);
	for (int i=0; i<ids.size(); i++) {
		log(LOG_DEBUG) << "current i: " << i;
		log(LOG_DEBUG) << "ids: " << ids[i];
	}
	Task deletedTask;
	string deletedTaskString;
	// delete the task both in task records and in displayed task list
	for (int i=0; i<ids.size(); i++) {
		int indexInDisplayedRecords = displayedRecords.getIndexByTaskId(ids[i]);
		int indexInTaskRecords = taskRecords.getIndexByTaskId(ids[i]);
		Task deletedTask = displayedRecords.getData(indexInDisplayedRecords);
		deletedTaskString = '"' + deletedTask.toScreenString() + '"';
		taskRecords.delData(indexInTaskRecords);
		displayedRecords.delData(indexInDisplayedRecords);
	}
	if (ids.size() == 1) {
		log(LOG_DEBUG) << "deleted task to string: " << deletedTask.toScreenString();
		log(LOG_DEBUG) << "saved deleted task string: " << deletedTaskString;
		sprintf_s(buffer, CmdDel::DEL_EXEC.c_str(), deletedTaskString.c_str());
	}
	else {
		sprintf_s(buffer, CmdDel::DEL_EXEC.c_str(), string("Tasks").c_str());
	}
	setStatusDisplay(buffer);
	setMainDisplay(displayedRecords.toString());
	return buffer;
}

//@author A0105541U
string Taak::executeClearCommand(CmdClear* cmd) {
	log(LOG_INFO) << "Taak::executeClearCommand - Begin to clear all task records";
	taskRecords.delDataAll();
	displayedRecords.delDataAll();
	sprintf_s(buffer, "%s", CmdClear::CLEAR_EXEC.c_str());
	setStatusDisplay(buffer);
	setMainDisplay(displayedRecords.toString());
	setMainDisplayTitle(CLEAR_COMMAND_STATUS);
	return buffer;
}

//@author A0105541U
string Taak::executeDisplayCommand(CmdDisplay* cmd) {
	setStatusDisplay(DISPLAY_COMMAND_STATUS);
    displayHomePage();
	return getStatusDisplay();
}

//@author A0105541U
string Taak::executeUpdateCommand(CmdUpdate* cmd) {

	int id = cmd->getId();

	int updateIndex = taskRecords.getIndexByTaskId(id);
	// if it is update SELECT, auto populate the orignial task into input box
	if (cmd->getUdtType() == UdtType::SELECT) {
		Task updateTask = taskRecords.getData(updateIndex);
		setStatusDisplay(UPDATE_COMMAND_1_STATUS);
		setInputDisplay(cmd->toString() + ' ' + updateTask.toHTMLString());
	}
	// else modify the task acoordingly
	else {
		Task modifiedTask = cmd->getModifiedTask();
		Task oldTask = taskRecords.getData(updateIndex);
		taskRecords.setData(updateIndex, modifiedTask);
		taskRecords.updateFile();
		sprintf_s(buffer, UPDATE_COMMAND_2_STATUS.c_str(), oldTask.toScreenString().c_str(), modifiedTask.toScreenString().c_str());
		setStatusDisplay(buffer);

		int displayedIndex = displayedRecords.getIndexByTaskId(id);

		displayedRecords.setData(displayedIndex, modifiedTask);
		setMainDisplay(displayedRecords.toString());
	}

	return "";
}

// @author A0105541U
string Taak::executeInvalidCommand(CmdInvalid* cmd) {
	string errorInfo = CmdInvalid::ERROR_MSG[cmd->getErrorType()]; //
	sprintf_s(buffer, errorInfo.c_str());
	setStatusDisplay(errorInfo);
	return buffer;
}

//@author A0105541U
string Taak::executeDoneCommand(CmdDone* cmd){
	vector<int> ids;
	myCopyVector(ids, cmd->ids);
	Task markedTask;
	string result;
	for (int i = 0; i < ids.size(); i++) {
		int indexInDisplayedRecords = displayedRecords.getIndexByTaskId(ids[i]);
		int indexInTaskRecords = taskRecords.getIndexByTaskId(ids[i]);
		markedTask = displayedRecords.getData(indexInDisplayedRecords);
		// if the command is to mark a task as done
		if (cmd->getDoneType() == CmdDone::DoneType::MARK_DONE){
			markedTask.setDone();
			result = CmdDone::DONE_EXEC;
		}
		// if the command is to mark a task as not done
		else if (cmd->getDoneType() == CmdDone::DoneType::MARK_UNDONE){
			markedTask.setUndone();
			result = CmdDone::UNDONE_EXEC;
		}
		taskRecords.setData(indexInTaskRecords, markedTask);
		taskRecords.updateFile();
		displayedRecords.setData(indexInDisplayedRecords, markedTask);
	}
	if (ids.size() <= 0) throw "invalid args";
	assert(ids.size() > 0);
	if (ids.size() == 1) {
		sprintf_s(buffer, result.c_str(), string('"' + markedTask.toScreenString() + '"').c_str());
	}
	else {
		sprintf_s(buffer, result.c_str(), string("Tasks").c_str());
	}
	setStatusDisplay(buffer);
	setMainDisplay(displayedRecords.toString());
	return buffer;
}

//@author A0101728M
/*	returns tasks that fulfil any of the search criteria
	checks if there are dates in the search string
		if two dates, check for tasks that are between those dates (inclusive)
		if one date, check for tasks ongoing on that date (includes tasks that start earlier AND end later)
	performs substring search for name and category (also checks if the dates are in the task name or categories)
	also does fuzzy matching for task names
	checks if search term is an attribute like done, undone, due, overdue
	note: search performs (fuzzy matching) and (substring matching) but not (fuzzy and substring matching).
*/
string Taak::executeSearchCommand(CmdSearch* cmd) {
	vector<string> keywords;
	myCopyVector(keywords, cmd->keywords);
	
	Records results = Records();

	vector<string> keywordsWithoutTags;
	vector<string> tags;
	Parser::findTags(keywords, &tags, &keywordsWithoutTags);
	for (int i = 0; i < tags.size(); i++){
		results.append(taskRecords.filterByCategory(true, boost::algorithm::to_lower_copy(tags[i])));
	}

	vector<Date> datesFound;
	vector<string> notDates;
	int numDates = Parser::findDates(keywordsWithoutTags, &datesFound, &notDates);
	if (numDates == 2){
		results.append(taskRecords.filterByDateBetween(true, datesFound[0], datesFound[1]));
		results.append(displayedRecords.filterBySameDay(true, datesFound[0]));
		results.append(displayedRecords.filterBySameDay(true, datesFound[1]));
	}
	else if (numDates == 1){
		results.append(taskRecords.filterBySameDay(true, datesFound[0]));
	}

	string keyword;
	string keywordWithoutDates;

	//combine keywords with dates into one search string without spaces
	//for case where the dates are in the task name
	if (keywords.size() > 1){
		for (int i = 0; i < keywords.size(); i++){
			if (keywords[i] != ""){
				keyword += keywords[i];
			}
		}
	}
	else{
		keyword = keywords[0];
	}

	//combine keywords without dates in one search string without spaces
	//for case where the dates are not in the task name
	if (notDates.size() > 1){
		for (int i = 0; i < notDates.size(); i++){
			if (notDates[i] != ""){
				keywordWithoutDates += notDates[i];
			}
		}
	}
	else if (notDates.size() == 1){
		keywordWithoutDates = notDates[0];
	}

	boost::algorithm::to_lower(keyword);
	boost::algorithm::to_lower(keywordWithoutDates);

	//can keep appending because 'append' prevents duplicates
	if (!keyword.empty()){
		results.append(taskRecords.filterByTaskNameContains(true, keyword));
		results.append(taskRecords.filterByCategory(true, keyword));
		results.append(taskRecords.filterByFuzzyContains(true, keyword, DEFAULT_FUZZY_THRESHOLD));
		if (keyword == CmdSearch::DONE){
			results.append(taskRecords.filterByDone(true, true));
		}
		else if (keyword == CmdSearch::UNDONE){
			results.append(taskRecords.filterByDone(true, false));
		}
		else if (keyword == CmdSearch::DUE){
			Date today = Date::now();
			results.append(taskRecords.filterByDateAfter(true, today));
		}
		else if (keyword == CmdSearch::OVERDUE){
			results.append(taskRecords.filterByOverdue(true, true).sortByDateEnd(true));
		}
		else if (keyword == CmdSearch::ALL){
			results.append(taskRecords);
		}
	}

	if (!keywordWithoutDates.empty()){
		results.append(taskRecords.filterByTaskNameContains(true, keywordWithoutDates));
		results.append(taskRecords.filterByCategory(true, keywordWithoutDates));
		results.append(taskRecords.filterByFuzzyContains(true, keywordWithoutDates, DEFAULT_FUZZY_THRESHOLD));
		if (keywordWithoutDates == CmdSearch::DONE){
			results.append(taskRecords.filterByDone(true, true));
		}
		else if (keywordWithoutDates == CmdSearch::UNDONE){
			results.append(taskRecords.filterByDone(true, false));
		}
		else if (keywordWithoutDates == CmdSearch::DUE){
			Date today = Date::now();
			results.append(taskRecords.filterByDateAfter(true, today));
		}
		else if (keywordWithoutDates == CmdSearch::OVERDUE){
			results.append(taskRecords.filterByOverdue(true, true).sortByDateEnd(true));
		}
		else if (keywordWithoutDates == CmdSearch::ALL){
			results.append(taskRecords);
		}
	}

	displayedRecords = results;
	setMainDisplayTitle(SEARCH_RESULTS_TITLE);
	if (results.empty()){
		setMainDisplay(NO_RESULTS_DISPLAY);
	}
	else {
		setMainDisplay(results.toString());
	}
	setStatusDisplay(cmd->toString());
	return buffer;
}

/*	accepts optional keyword asc/desc to determine order
	if no keyword is detected, assumes sort order of ascending
*/
string Taak::executeSortCommand(CmdSort* cmd){
	vector<string> keywords;
	myCopyVector(keywords, cmd->keywords);
	
	Records results = Records();
	bool ascending = true;
	if (keywords.size() == 0){
		//default behavior
		results = displayedRecords.sortByNameAlphabetical(ascending);
	}
	else{
		//default sort with order
		if (keywords.size() == 1){
			if (keywords[0] == CmdSort::DESCENDING){
				ascending = false;
				keywords.erase(keywords.begin());
			}
			else if (keywords[0] == CmdSort::ASCENDING){
				keywords.erase(keywords.begin());
			}
			results = displayedRecords.sortByNameAlphabetical(ascending);
		}

		//for single-word sort terms
		else if (keywords.size() == 2){
			if (keywords[1] == CmdSort::DESCENDING){
				ascending = false;
				keywords.erase(keywords.begin() + 1);
			}
			else if (keywords[1] == CmdSort::ASCENDING){
				keywords.erase(keywords.begin() + 1);
			}
		}
			//for two-word sort terms
		else if (keywords.size() > 2){
			if (keywords[2] == CmdSort::DESCENDING){
				ascending = false;
				keywords.erase(keywords.begin() + 2);
			}
			else if (keywords[2] == CmdSort::ASCENDING){
				keywords.erase(keywords.begin() + 2);
			}
		}

		string sortType = Parser::concatenate(keywords);
		if (sortType == CmdSort::OVERDUE){
			results = displayedRecords.sortByOverdue(ascending);
		}
		else if (sortType == CmdSort::DONE){
			results = displayedRecords.sortByDone(ascending);
		}
		else if (sortType == CmdSort::UNDONE){
			results = displayedRecords.sortByDone(!ascending);
		}
		else if (sortType == CmdSort::NAME){
			results = displayedRecords.sortByNameAlphabetical(ascending);
		}
		else if (sortType == CmdSort::NAME_LENGTH){
			results = displayedRecords.sortByNameLength(ascending);
		}
		else if (sortType == CmdSort::DATE_START){
			results = displayedRecords.sortByDateStart(ascending);
		}
		else if (sortType == CmdSort::DATE_END || sortType == CmdSort::DATE || sortType == CmdSort::DUE){
			results = displayedRecords.sortByDateEnd(ascending);
		}
	}
	displayedRecords = results;
    setMainDisplayTitle(SORT_RESULTS_TITLE);
	setMainDisplay(results.toString());
	setStatusDisplay(cmd->toString());
	return buffer;
}

/*	same as search command, except that
		it acts on displayed results, and
		checks for an optional keyword - in/out
	if no keyword detected, assumes filter in
*/
string Taak::executeFilterCommand(CmdFilter* cmd){
	vector<string> keywords;
	myCopyVector(keywords, cmd->keywords);

	Records results = Records();
	bool in = true;

	//use IN/OUT flag if present
	if (boost::algorithm::to_lower_copy(keywords[0]) == CmdFilter::OUT_){
		log(LOG_DEBUG) << "Taak::ExecuteFilterCommand - Keyword 'out' detected";
		in = false;
		keywords.erase(keywords.begin());
	}
	else if (boost::algorithm::to_lower_copy(keywords[0]) == CmdFilter::IN_){
		log(LOG_DEBUG) << "Taak::ExecuteFilterCommand - Keyword 'in' detected";
		keywords.erase(keywords.begin());
	}
	else {
		log(LOG_DEBUG) << "Taak::ExecuteFilterCommand - No keyword detected. Assumes filter in.";
	}

	vector<string> keywordsWithoutTags;
	vector<string> tags;
	Parser::findTags(keywords, &tags, &keywordsWithoutTags);

	vector<Date> datesFound;
	vector<string> notDates;
	int numDates = Parser::findDates(keywordsWithoutTags, &datesFound, &notDates);

	string keyword;
	string keywordWithoutDates;

	//combine keywords with dates into one search string without spaces
	//for case where the dates are in the task name
	if (keywords.size() > 1){
		for (int i = 0; i < keywords.size(); i++){
			if (keywords[i] != ""){
				keyword += keywords[i];
			}
		}
	}
	else {
		keyword = keywords[0];
	}

	//combine keywords without dates in one search string without spaces
	//for case where the dates are not in the task name
	if (notDates.size() > 1){
		for (int i = 0; i < notDates.size(); i++){
			if (notDates[i] != ""){
				keywordWithoutDates += notDates[i];
			}
		}
	}
	else if (notDates.size() == 1){
		keywordWithoutDates = notDates[0];
	}

	boost::algorithm::to_lower(keyword);
	boost::algorithm::to_lower(keywordWithoutDates);

	if (in){
		log(LOG_DEBUG) << "Taak::ExecuteFilterCommand - Filtering in";
		for (int i = 0; i < tags.size(); i++){
			results.append(displayedRecords.filterByCategory(in, boost::algorithm::to_lower_copy(tags[i])));
		}

		if (numDates == 2){
			results.append(displayedRecords.filterByDateBetween(in, datesFound[0], datesFound[1]));
			results.append(displayedRecords.filterBySameDay(in, datesFound[0]));
			results.append(displayedRecords.filterBySameDay(in, datesFound[1]));
		}
		else if (numDates == 1){
			results.append(displayedRecords.filterBySameDay(in, datesFound[0]));
		}

		if (notDates.size() > 0){
			if (!keyword.empty()){
				results.append(displayedRecords.filterByTaskNameContains(in, keyword));
				results.append(displayedRecords.filterByCategory(in, keyword));
				results.append(displayedRecords.filterByFuzzyContains(in, keyword, DEFAULT_FUZZY_THRESHOLD));
				if (keyword == CmdSearch::DONE){
					results.append(displayedRecords.filterByDone(in, true));
				}
				else if (keyword == CmdSearch::UNDONE){
					results.append(displayedRecords.filterByDone(in, false));
				}
				else if (keyword == CmdSearch::DUE){
					Date today = Date::now();
					results.append(displayedRecords.filterByDateAfter(in, today));
				}
				else if (keyword == CmdSearch::OVERDUE){
					results.append(displayedRecords.filterByOverdue(in, true).sortByDateEnd(true));
				}
				else if (keyword == CmdSearch::ALL){
					results.append(displayedRecords);
				}
			}

			if (!keywordWithoutDates.empty()){
				results.append(displayedRecords.filterByTaskNameContains(in, keywordWithoutDates));
				results.append(displayedRecords.filterByCategory(in, keywordWithoutDates));
				results.append(displayedRecords.filterByFuzzyContains(in, keyword, DEFAULT_FUZZY_THRESHOLD));
				if (keywordWithoutDates == CmdSearch::DONE){
					results.append(displayedRecords.filterByDone(in, true));
				}
				else if (keywordWithoutDates == CmdSearch::UNDONE){
					results.append(displayedRecords.filterByDone(in, false));
				}
				else if (keywordWithoutDates == CmdSearch::DUE){
					Date today = Date::now();
					results.append(displayedRecords.filterByDateAfter(in, today));
				}
				else if (keywordWithoutDates == CmdSearch::OVERDUE){
					results.append(displayedRecords.filterByOverdue(in, true).sortByDateEnd(true));
				}
				else if (keywordWithoutDates == CmdSearch::ALL){
					results.append(displayedRecords);
				}
			}
		}
	}
	else {
		log(LOG_DEBUG) << "Taak::ExecuteFilterCommand - Filtering out";
		results = displayedRecords;

		for (int i = 0; i < tags.size(); i++){
			results = results.filterByCategory(in, boost::algorithm::to_lower_copy(tags[i]));
		}

		if (numDates == 2){
			results = results.filterByDateBetween(in, datesFound[0], datesFound[1]);
		}
		else if (numDates == 1){
			results = results.filterBySameDay(in, datesFound[0]);
		}

		if (notDates.size() > 0){
			if (!keyword.empty()){
				results = results.filterByTaskNameContains(in, keyword);
				results = results.filterByCategory(in, keyword);
				results = results.filterByFuzzyContains(in, keyword, DEFAULT_FUZZY_THRESHOLD);
				if (keyword == CmdFilter::DONE){
					results = results.filterByDone(in, true);
				}
				else if (keyword == CmdFilter::UNDONE){
					results = results.filterByDone(in, false);
				}
				else if (keyword == CmdFilter::DUE){
					Date today = Date::now();
					results = results.filterByDateAfter(in, today);
				}
				else if (keyword == CmdFilter::OVERDUE){
					results = results.filterByOverdue(in, true);
				}
			}

			if (!keywordWithoutDates.empty()){
				results = results.filterByTaskNameContains(in, keywordWithoutDates);
				results = results.filterByCategory(in, keywordWithoutDates);
				results = results.filterByFuzzyContains(in, keyword, DEFAULT_FUZZY_THRESHOLD);
				if (keywordWithoutDates == CmdFilter::DONE){
					results = results.filterByDone(in, true);
				}
				else if (keywordWithoutDates == CmdFilter::UNDONE){
					results = results.filterByDone(in, false);
				}
				else if (keywordWithoutDates == CmdFilter::DUE){
					Date today = Date::now();
					results = results.filterByDateAfter(in, today);
				}
				else if (keywordWithoutDates == CmdFilter::OVERDUE){
					results = results.filterByOverdue(in, true);
				}
			}
		}
	}

	displayedRecords = results;
	
	setMainDisplayTitle(FILTER_RESULT_TITLE);
	if (results.empty()){
		setMainDisplay(NO_RESULTS_DISPLAY);
	}
	else {
		setMainDisplay(results.toString());
	}
	setStatusDisplay(cmd->toString());
	return buffer;
}

//@author A0105541U
// execute commands in history that has an index within [start, end)
void Taak::executeCommandHistory(int start, int end) {
	CmdAdd * cmdAdd;
	CmdDel * cmdDel;
	CmdSearch *cmdSearch;
	CmdClear *cmdClear;
	CmdUpdate *cmdUpdate;
	CmdDone *cmdDone;
	CmdHelp *cmdHelp;
	CmdSort *cmdSort;
	CmdFilter *cmdFilter;
	CmdDisplay *cmdDisplay;
	Cmd* cmd;
	for (int p = start; p < end; p++) {
		cmd = commandHistory.getCommand(p);
		cmdTypes commandType = cmd->getCmdType();
		switch (commandType) {
			case cmdTypes::ADD:
				cmdAdd = (CmdAdd*) cmd;
				executeAddCommand(cmdAdd);
				break;
			case cmdTypes::DEL:
				cmdDel = static_cast<CmdDel*>(cmd);
				log(LOG_DEBUG) << "about to manipulate the cmdDel that is passed a bit";
				
				log(LOG_DEBUG) << "size of modified ids: " << cmdDel->ids.size();
				
				executeDeleteCommand(cmdDel);
				break;
			case cmdTypes::CLEAR:
				cmdClear = (CmdClear*)cmd;
				executeClearCommand(cmdClear);
				break;
			case cmdTypes::UPDATE:
				cmdUpdate = (CmdUpdate*)cmd;
				executeUpdateCommand(cmdUpdate);
				break;
			case cmdTypes::DONE:
				cmdDone = (CmdDone*)cmd;
				executeDoneCommand(cmdDone);
				break;
			case cmdTypes::UNDONE:
				cmdDone = (CmdDone*)cmd;
				executeDoneCommand(cmdDone);
				break;
			case cmdTypes::HELP:
				cmdHelp = (CmdHelp*)cmd;
				executeHelpCommand(cmdHelp);
				break;
			case cmdTypes::SEARCH:
				cmdSearch = (CmdSearch*)cmd;
				executeSearchCommand(cmdSearch);
				break;
			case cmdTypes::SORT:
				cmdSort = (CmdSort*)cmd;
				executeSortCommand(cmdSort);
				break;
			case cmdTypes::FILTER:
				cmdFilter = (CmdFilter*)cmd;
				executeFilterCommand(cmdFilter);
				break;
			case cmdTypes::DISPLAY:
				cmdDisplay = (CmdDisplay*)cmd;
				executeDisplayCommand(cmdDisplay);
				break;

		}

		log(LOG_INFO) << "executing command: "<< cmd->toString();
		log(LOG_DEBUG) << displayedRecords.toString();
	}
}

//@author A0105541U
string Taak::executeRedoCommand() {
	int steps = 1; // will get from cmd
	int currentCommandIndex = commandHistory.getNumCommand();
	commandHistory.redo(steps);
	int endCommandIndex = commandHistory.getNumCommand();

	if (currentCommandIndex == endCommandIndex) {
		setStatusDisplay(NOTHING_REDO_STATUS);
	} else {
		executeCommandHistory(currentCommandIndex, endCommandIndex);
		setStatusDisplay(REDO_FINISH_STATUS);
	}
	return getStatusDisplay();

}

string Taak::executeUndoCommand() {
	int steps = 1;  // will get from cmd
	if (commandHistory.getNumCommand() == 1) {
		setStatusDisplay(NOTHING_UNDO_STATUS);
	} else {
		log(LOG_DEBUG) << "about to call cmdHistory.undo";
		commandHistory.undo(steps);
		// Undo will execute command from 0 to numCommand-1 in commandHistory
		int numCommand = commandHistory.getNumCommand();
		taskRecords = initialRecords;
		taskRecords.updateFile();

		log(LOG_DEBUG) << "about to call executeCmdHistory()";
		executeCommandHistory(0, numCommand);
		setStatusDisplay(UNDO_FINISH_STATUS);
	}
	return getStatusDisplay();
}
//@author A0078695H
string Taak::executeHelpCommand(CmdHelp* cmd) {
	string helpInfo = CmdHelp::toHelpInfoHtml(cmd->getHelpType());
	setMainDisplay(helpInfo);
	setStatusDisplay(HELP_COMMAND_STATUS);
	setMainDisplayTitle("");
	return helpInfo;
}

//@author A0105541U
string Taak::executeCommand(auto_ptr<Cmd> cmd){
	cmdTypes commandType = cmd->getCmdType();
	string st;
	Cmd* a;
	Cmd *b;
	CmdSearch *cmdSearch;
	vector<string>* temp; 
	switch (commandType) {
		case cmdTypes::ADD:
			a = static_cast<CmdAdd*>(cmd.get());
			b = (CmdAdd*) malloc(sizeof(CmdAdd));
			memcpy(b,a,sizeof(CmdAdd));
			commandHistory.addCommand(b);
			st = executeAddCommand((CmdAdd*)b);

			break;
		case cmdTypes::DEL:
			a = static_cast<CmdDel*>(cmd.get());
			b = (CmdDel*) malloc(sizeof(CmdDel));
			memcpy(b,a,sizeof(CmdDel));
			
			st = executeDeleteCommand((CmdDel*)b);
			log(LOG_DEBUG) << "in executeCommand, delete switch. Copied a to b";
			log(LOG_DEBUG) << "will try to copy vector later";
			log(LOG_DEBUG) << "size of b's vector size: " << ((CmdDel*)b)->getIds().size();
			commandHistory.addCommand(b);
			break;
		case cmdTypes::DISPLAY:
			a = static_cast<CmdDisplay*>(cmd.get());
			b = (CmdDisplay*) malloc(sizeof(CmdDisplay));
			memcpy(b,a,sizeof(CmdDisplay));
			st = executeDisplayCommand((CmdDisplay*)b);
			commandHistory.addCommand(b);
			break;
		case cmdTypes::CLEAR:
			a = static_cast<CmdClear*>(cmd.get());
			b = (CmdClear*) malloc(sizeof(CmdClear));
			memcpy(b,a,sizeof(CmdClear));

			st = executeClearCommand((CmdClear*)b);
			commandHistory.addCommand(b);
			break;
		case cmdTypes::SORT:
			a = static_cast<CmdSort*>(cmd.get());
			b = (CmdSort*) malloc(sizeof(CmdSort));
			memcpy(b,a,sizeof(CmdSort));

			st = executeSortCommand((CmdSort*)b);
			commandHistory.addCommand(b);
			break;
		case cmdTypes::SEARCH:
			a = static_cast<CmdSearch*>(cmd.get());
			b = (CmdSearch*) malloc(sizeof(CmdSearch));
			memcpy(b,a,sizeof(CmdSearch));

			st = executeSearchCommand((CmdSearch*)b);
			commandHistory.addCommand(b);
			break;
		case cmdTypes::FILTER:
			a = static_cast<CmdFilter*>(cmd.get());
			b = (CmdFilter*) malloc(sizeof(CmdFilter));
			memcpy(b,a,sizeof(CmdFilter));
			st = executeFilterCommand((CmdFilter*)b);
			commandHistory.addCommand(b);
			break;
		case cmdTypes::UPDATE:
			st = executeUpdateCommand((static_cast<CmdUpdate*>(cmd.get())));
			if (static_cast<CmdUpdate*>(cmd.get())->getUdtType() == UdtType::EDIT) {
				a = static_cast<CmdUpdate*>(cmd.get());
				b = (CmdUpdate*) malloc(sizeof(CmdUpdate));
				memcpy(b,a,sizeof(CmdUpdate));
				commandHistory.addCommand(b);
			}
			break;
		case cmdTypes::DONE:
			a = static_cast<CmdDone*>(cmd.get());
			b = (CmdDone*) malloc(sizeof(CmdDone));
			memcpy(b,a,sizeof(CmdDone));

			commandHistory.addCommand(b);
			st = executeDoneCommand((CmdDone*)b);

			break;
		case cmdTypes::UNDONE:
			a = static_cast<CmdDone*>(cmd.get());
			b = (CmdDone*) malloc(sizeof(CmdDone));
			memcpy(b,a,sizeof(CmdDone));

			commandHistory.addCommand(b);
			st = executeDoneCommand((CmdDone*)b);

			break;
		case cmdTypes::HELP:
			a = static_cast<CmdHelp*>(cmd.get());
			b = (CmdHelp*) malloc(sizeof(CmdHelp));
			memcpy(b,a,sizeof(CmdHelp));

			commandHistory.addCommand(b);
			st = executeHelpCommand((CmdHelp*)b);

			break;

		case cmdTypes::INVALID:
			return executeInvalidCommand((static_cast<CmdInvalid*>(cmd.get())));
			break;
		case cmdTypes::UNDO:
			st = executeUndoCommand();
			break;
		case cmdTypes::REDO:
			st = executeRedoCommand();
			break;
		case EXIT:
			setStatusDisplay("exit");
			exit(0);
			break;
		default:
			return executeInvalidCommand(&(CmdInvalid(cmdTypes::INVALID, vector<string>(), cmd.get()->toString(), ErrorType::ERROR_UNRECOG_CMD)));
	}
	return (getStatusDisplay() + "\n" + getMainDisplay());
}

//@author A0096566M
string Taak::getMainDisplay(){
	return this->mainDisplay;
}
//@author 
string Taak::getMainDisplayTitle(){
    return this->mainDisplayTitle;
}
//@author A0096566M
string Taak::getStatusDisplay(){
	return this->statusDisplay;
}
//@author A0096566M
vector<string> Taak::getFeedbackDisplay(){
	return this->feedbackDisplay;
}
//@author A0096566M
string Taak::getInputDisplay() {
	return this->inputDisplay;
}
//@author A0096566M
void Taak::setInputDisplay(string input) {
	this->inputDisplay = input;
}
//@author A0096566M
void Taak::setMainDisplay(string main){
	this->mainDisplay = main;
}
//@author 
void Taak::setMainDisplayTitle(string title){
	this->mainDisplayTitle = title;
}
//@author A0096566M
void Taak::setStatusDisplay(string status){
	this->statusDisplay = status;
}
//@author A0096566M
void Taak::setFeedbackDisplay(vector<string> feedback){
	this->feedbackDisplay = feedback;
}
//@author A0078695H
Parser Taak::getParser(){
	return parser;
}

//@author A0078695H
template <typename T> void Taak::myCopyVector(vector<T>& to, vector<T>& from) {
	log(LOG_DEBUG) << "Taak::myCopyVector - size of from.size: " << from.size();
	to = vector<T>();
	for (int i=0; i<from.size(); i++) {
		to.push_back(from[i]);
	}
}